/*
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 *
 * Copyright @2008 the original author or authors.
 */

package com.bluestone.util;

import java.awt.GraphicsEnvironment;
import java.awt.Robot;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.ResourceBundle;
import java.util.Stack;

import javax.imageio.ImageIO;

import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

import com.bluestone.core.Element;
import com.bluestone.core.Input;
import com.bluestone.core.Module;
import com.bluestone.core.Page;
import com.bluestone.core.Project;
import com.bluestone.core.Resource;
import com.bluestone.core.TestCase;
import com.bluestone.core.TestSuite;
import com.bluestone.run.ParserManager;

/**
 * @author <a href="mailto:bluestone.daniel.q@gmail.com">daniel.q</a>
 */
public class Util {
	static Robot robot = null;

	static Logger logger = null;

	static Project project = null;
	
	static Page forwardpage = null;
		
	static Stack stackPages = new Stack();
	
	static LinkedList linkActions = new LinkedList();
	
	public static int SPEEDRATE = 100;

	public static int DELAY100 = 100;

	public static int DELAY200 = 200;

	public static int DELAY300 = 300;

	public static int DELAY400 = 400;

	public static int DELAY500 = 500;

	public static int DELAY600 = 600;

	public static int DELAY700 = 700;

	public static int DELAY800 = 800;

	public static int DELAY900 = 900;

	public static int DELAY1000 = 1000;

	public static int DELAY2000 = 2000;

	public static int DELAY3000 = 3000;

	public static int INPUTTEXTDELAY = 300;

	public static int MOVEDELAY = 500;

	public static int CLICKDELAY = 500;

	public static int CHECKDELAY = 2000;

	public static int CLOSEPAGEDELAY = 1000;

	public static int INPUTKEYDELAY = 500;

	public static int MAXPAGEDELAY = 1500;

	public static int CLICKURLDELAY = 3000;

	public static int FOCUSDELAY = 300;

	public static int RUNDELAY = 300;
	public static int SAVEASDELAY = 500;
	public static int COMPAREIMAGEDELAY = 300;
	
	public static int MINPAGEDELAY = 1000;
	
	public static int CALLFUNCTIONDELAY = 500;

	public static String ERROR1 = "Can't find object ";
	
	private static long actionId = 0;
	private static long projectId =0;
	private static long testSuitId = 0;
	private static long testCaseId = 0;
	private static long commandId = 0;
	private static long reportBeanId = 0;
	public static String ACTION_ID="ACTIONID";
	public static String PROJECT_ID="PROJECTID";
	public static String TESTSUIT_ID="TESTSUITID";
	public static String TESTCASE_ID="TESTCASEID";
	public static String COMMAND_ID="COMMANDID";
	public static String REPORTBEAN_ID="REPORTBEANID";
	
	private static HashMap resources = new HashMap();

	public static int getDelayTime(int ms) {
		if (project != null) {
			return Integer.parseInt(project.getSpeedrate()) * ms / 100;
		} else {
			return SPEEDRATE * ms / 100;
		}
	}

	public static void error(Object obj) {
		getLogger().error(getLogObject(obj));
	}

	public static void info(Object obj) {
		getLogger().info(getLogObject(obj));
	}
	
	public static void warn(Object obj){
		getLogger().warn(getLogObject(obj));		
	}

	private static Object getLogObject(Object e) {

		if (org.xml.sax.SAXParseException.class.isInstance(e)) {
			org.xml.sax.SAXParseException me = (org.xml.sax.SAXParseException) e;
			String str = me.getSystemId() + ":" + me.getLineNumber() + ":"
					+ me.getColumnNumber() + ":" + me.getMessage();
			return str;
		} else if (java.lang.NullPointerException.class.isInstance(e)) {
			java.lang.NullPointerException me = (java.lang.NullPointerException) e;
			me.printStackTrace();
			return me;
		} else if (Project.class.isInstance(e)) {
			Project project = (Project) e;
			StringBuffer buffer = new StringBuffer();
			buffer.append(project.toString() + "\r\n");
			buffer.append(project.getBrowser().toString() + "\r\n");
			int totalpages = 0;
			int totalelements = 0;
			int totalmodule = 0;
			int totaltestsuite = 0;
			int totaltestcase = 0;
			
			for (int i = 0; i < project.getInputList().size(); i++) {
				int moduleelements = 0;
				Object obj = ((Input) project.getInputList().get(i))
						.getObject();
				if (Module.class.isInstance(obj)) {
					totalmodule++;
					Module module = (Module) obj;
					buffer.append(module.toString() + "\r\n");
					for (int j = 0; j < module.getPages().size(); j++) {
						Page page = (Page) module.getPages().get(j);
						totalpages++;
						buffer.append(module.getName() + " " + page.toString()
								+ "element count:" + page.getElements().size()
								+ "\r\n");

						for (int k = 0; k < page.getElements().size(); k++) {
							Element element = (Element) page.getElements().get(
									k);
							totalelements++;
							moduleelements++;
							buffer.append(page.getName() + " "
									+ element.toString() + "\r\n");
						}
					}
					buffer.append(module.getName() + " page count:"
							+ module.getPages().size() + " element count:"
							+ moduleelements + "\r\n");
				}else if(TestSuite.class.isInstance(obj)){
					totaltestsuite ++;
					TestSuite testsuite = (TestSuite)obj;
					buffer.append(testsuite.toString() + "\r\n");
					int testcasecount = 0;
					for (int j = 0; j < testsuite.getTestcaseList().size(); j++) {
						TestCase testcase = (TestCase) testsuite.getTestcaseList()
								.get(j);
						totaltestcase++;
						testcasecount++;
						buffer.append(testsuite.getName() + " "
								+ testcase.toString() + "\r\n");

					}
					buffer.append(testsuite.getName() + " testcase count:" + testcasecount
							+ "\r\n");										
				}
				
			}
			buffer.append("total module count:" + totalmodule
					+ " total page count:" + totalpages
					+ " total element count:" + totalelements + "\r\n");
			buffer.append("total testsuite count:"
					+ totaltestsuite
					+ " total testcase count:" + totaltestcase + "\r\n");
			
			
						
			return buffer.toString();

		}

		return e;
	}

	public static java.awt.Robot getRobot() {
		if (robot != null)
			return robot;
		GraphicsEnvironment ge = GraphicsEnvironment
				.getLocalGraphicsEnvironment();
		try {
			robot = new java.awt.Robot(ge.getDefaultScreenDevice());
		} catch (Exception me) {
			me.printStackTrace();
		}
		return robot;
	}

	public static Logger getLogger() {
		if (logger != null)
			return logger;
		PropertyConfigurator.configure("log4j.properties");
		logger = Logger.getLogger("log4j");
		return logger;
	}

	/**
	 * Generate the project from the given file path.
	 * @return project - Project
	 */
	public static Project getProject(String file) {
		ParserManager parser = new ParserManager();
		project = parser.parseProject(file);
		if (project != null){
			initResources(project);
			Util.info(project);
		}
		return project;
	}

	public static Project getCurrentProject() {
		return project;
	}
	
	public static void setCurrentProject(Project p){
		project = p;		
	}
	
	private static void initResources(Project project){
		ArrayList list = project.getResources();
		for(int i =0;i<list.size();i++){
			Resource resource = (Resource)list.get(i);
			ResourceBundle rb = ResourceBundle.getBundle(resource.getName(),project.getLocale());
			if(rb==null){
				warn("The resource file of"+resource.getName()+" is not exsit.");
				continue;
			}
			resources.put(resource.getKey(), rb);
		}
	}
	
	public static Page getCurrentPage(){
		if(stackPages.empty()){
			return null;
		}
		Page page = (Page)stackPages.peek();
        return page;
	}
	public static LinkedList getLinkActions(){
		return  linkActions;
	}
	
	public static void setCurrentPage(Page page){
		stackPages.push(page);
	}
	
	public static void removeCurrentPage(){
		stackPages.pop();
	}
	
	public static void clearPage(){
		stackPages.clear();
	}
	
	/**
	 * Compare to the BufferedImage acccording to the rgb.
	 * @param bi1 BufferedImage
	 * @param bi2 BufferedImage
	 * @return true if the two bufferedIamge is equal.
	 */
	public static boolean compareBufferedImage(BufferedImage bi1, BufferedImage bi2) {
		try {
			int h1 = bi1.getHeight();
			int w1 = bi1.getWidth();
			int h2 = bi2.getHeight();
			int w2 = bi2.getWidth();
			if (h1 != h2 || w1 != w2) {
				return false;
			}
			for (int i = 0; i < w1; i++) {
				for (int j = 0; j < h1; j++) {
					int biRGB1 = bi1.getRGB(i, j);
					int biRGB2 = bi2.getRGB(i, j);
					if (biRGB1 != biRGB2) {
						return false;
					}
				}
			}
		} catch (Exception e) {
			Util.error(e);
			return false;
		}
		return true;
	}

	/**
	 * Compare to the two image from the given path.
	 * @param imageFilePath1 String that defines the path of the image1
	 * @param imageFilePath2 String that defines the path of the image2
	 * @return true if the two image is equal.
	 */
	public static boolean compareImage(String imageFilePath1, String imageFilePath2) {
		
		BufferedImage bi1;
		BufferedImage bi2;
		try {
			bi1 = ImageIO.read(new File(imageFilePath1));
		    bi2 = ImageIO.read(new File(imageFilePath2));
		} catch (IOException e) {
			Util.error(e);
			return false;
		}
		return compareBufferedImage(bi1,bi2);
	}
	
	/**
	 * 
	 * @return the unique id for the action in the current project.
	 */
	public static String generateActionId(){
		return generateId(Util.ACTION_ID);
	}
	/**
	 * 
	 * @return the unique id for the project in the current project.
	 */
	public static String generateProjectId(){
		return generateId(Util.PROJECT_ID);
	}
	/**
	 * 
	 * @return the unique id for the testSuite in the current project.
	 */
	public static String generateTestSuitId(){
		return generateId(Util.TESTSUIT_ID);
	}
	/**
	 * 
	 * @return the unique id for the testCase in the current project.
	 */
	public static String generateTestCaseId(){
		return generateId(Util.TESTCASE_ID);
	}
	/**
	 * 
	 * @return the unique id for the command in the current project.
	 */
	public static String generateCommandId(){
		return generateId(Util.COMMAND_ID);
	}	
	/**
	 * 
	 * @return the unique id for the reportBean in the current project.
	 */
	public static String generateReportBeanId(){
		return generateId(Util.REPORTBEAN_ID);
	}
	/**
	 * Generate the uniqu id from the given type.
	 * @param type
	 * @return id - String
	 */
	public static String generateId(String type){
		StringBuffer sb = new StringBuffer();
		if(ACTION_ID.equals(type)){
			sb.append("action_");
			sb.append(System.currentTimeMillis()+actionId++);
		}else if(PROJECT_ID.equals(type)){
			sb.append("project_");
			sb.append(System.currentTimeMillis()+projectId++);
		}else if(Util.TESTCASE_ID.equals(type)){
			sb.append("testCase_");
			sb.append(System.currentTimeMillis()+testCaseId++);
		}else if(Util.TESTSUIT_ID.equals(type)){
			sb.append("testSuit_");
			sb.append(System.currentTimeMillis()+testSuitId++);
		}else if(Util.COMMAND_ID.equals(type)){
			sb.append("command_");
			sb.append(System.currentTimeMillis()+commandId++);
		}else if(Util.REPORTBEAN_ID.equals(type)){
			sb.append("report_");
			sb.append(reportBeanId++);
		}
		return sb.toString();
	}
	/**
	 * Invokes the underlying method represented by this Method object, on the specified object with the specified parameters.
	 * @param obj the object the underlying method is invoked from
	 * @param methodName the name of the method
	 * @param args the arguments used for the method call 
	 * @return the result of dispatching the method represented by this object on obj with parameters args 
	 */
	public static Object invokeClassOfMethod(Object obj, String methodName,
			Object[] args) {
		Class c = obj.getClass();
		Method[] methods = c.getMethods();
		if (methods == null) {
			return null;
		}
		for (int i = 0; i < methods.length; i++) {
			if (methodName.equals(methods[i].getName())) {
				try {
					return methods[i].invoke(obj, args);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
		return null;
	}
	
	/**
	 * Gets a string for the given key from the resource bundle or one of its parents.
	 * @param key
	 * @return value - String
	 */
	public static String getString(String key){
		int index = key.indexOf(".");
		String resourceKey=null;
		if(key.startsWith("@")){
			resourceKey = key.substring(1, index);	
		}else{
			resourceKey = key.substring(0, index);
		}
		String keyStr = key.substring(index+1);
		String value = keyStr;
		Object obj = resources.get(resourceKey);
		if(ResourceBundle.class.isInstance(obj)){
			ResourceBundle rb =(ResourceBundle)obj;
			value= rb.getString(keyStr);
		}
		
		return value;
	}
	
}
